using System;
using System.Collections.Generic;
using System.Linq;
using Lightray.Hattrick.ChppLib.EntityTypes;

// needs heavy refactoring
namespace Lightray.Hattrick.ChppLib
{
    public class Calculations
    {
        public enum TeamPosition
        {
            Training,
            Substitute,
            FirstTeam
        }

        private static bool isPlayerInPlayersList(IEnumerable<PlayerScores> lineup, Player player)
        {
            return getPlayerFromLineup(lineup, player) != null;
        }

        private static PlayerScores getPlayerFromLineup(IEnumerable<PlayerScores> lineup, Player player)
        {
            return lineup.FirstOrDefault(lineupPlayer => lineupPlayer.TargetPlayer == player);
        }

        private static PlayerScores getNextCandidate(IEnumerable<PlayerScores> lineup, List<PlayerScores> options)
        {
            return options.FirstOrDefault(candidate => !isPlayerInPlayersList(lineup, candidate.TargetPlayer));
        }

        private static void switchPlayerPositionAndAddReplacement(List<PlayerScores> lineup, PlayerScores playerToRemove, PlayerScores replacementPlayer, FieldPosition newPosition,
            List<PlayerScores> replacementOptions, List<PlayerScores> Goalkeepers, List<PlayerScores> Defenders,
            List<PlayerScores> Wingbacks, List<PlayerScores> Playmakers, List<PlayerScores> Wingers, List<PlayerScores> Forwards)
        {
            removePlayerFromLineup(lineup, playerToRemove.TargetPlayer, Goalkeepers, Defenders, Wingbacks, Playmakers, Wingers, Forwards);
            replacementPlayer.AssignedPosition = playerToRemove.AssignedPosition;
            replacementOptions.RemoveAt(0);
            playerToRemove.AssignedPosition = newPosition;
            addSelectedPlayerToLinup(lineup, playerToRemove, playerToRemove.AssignedPosition, Goalkeepers, Defenders, Wingbacks, Playmakers, Wingers, Forwards);
            addSelectedPlayerToLinup(lineup, replacementPlayer, replacementPlayer.AssignedPosition, Goalkeepers, Defenders, Wingbacks, Playmakers, Wingers, Forwards);
        }

        private static void RemovePlayerByName(PlayerScores playerToRemove, List<PlayerScores> listOfPlayers)
        {
            for (int i = 0; i < listOfPlayers.Count; i++)
            {
                if (playerToRemove.TargetPlayer == listOfPlayers[i].TargetPlayer)
                    listOfPlayers.RemoveAt(i);
            }
        }

        private static void AddPlayerToCandidates(PlayerScores NAS, List<PlayerScores> Goalkeepers, List<PlayerScores> Defenders, List<PlayerScores> Wingbacks, List<PlayerScores> Playmakers, List<PlayerScores> Wingers, List<PlayerScores> Forwards)
        {
            AddPlayerToList(NAS, Goalkeepers, FieldPosition.Goalkeeper);
            AddPlayerToList(NAS, Defenders, FieldPosition.Defender);
            AddPlayerToList(NAS, Wingbacks, FieldPosition.Wingback);
            AddPlayerToList(NAS, Playmakers, FieldPosition.Playmaker);
            AddPlayerToList(NAS, Wingers, FieldPosition.Winger);
            AddPlayerToList(NAS, Forwards, FieldPosition.Forward);
        }

        private static void AddPlayerToList(PlayerScores NAS, List<PlayerScores> playersList, FieldPosition listPosition)
        {
            if (!isPlayerInPlayersList(playersList, NAS.TargetPlayer))
            {
                playersList.Add(NAS);
                playersList.Sort((p1, p2) => p2.GetScoreByPosition(listPosition).CompareTo(p1.GetScoreByPosition(listPosition)));
            }
        }

        public static List<PlayerScores> GetLineup(List<Player> players, Weather weather, bool isCounter, bool removeFromListAfterPickedLineup,
            int howManyWingbacks, int howManyDefenders, int howManyWingers, int howManyPlaymakers, int howManyForwards)
        {
            #region init stuff
            if ((howManyDefenders + howManyForwards + howManyPlaymakers + howManyWingbacks + howManyWingers) > 10)
                throw new ArgumentException("Too many players in lineup");
            List<PlayerScores> Goalkeepers = new List<PlayerScores>();
            List<PlayerScores> Defenders = new List<PlayerScores>();
            List<PlayerScores> Wingbacks = new List<PlayerScores>();
            List<PlayerScores> Playmakers = new List<PlayerScores>();
            List<PlayerScores> Wingers = new List<PlayerScores>();
            List<PlayerScores> Forwards = new List<PlayerScores>();
            foreach (Player item in players)
            {
                if ((item.InjuryLevel > 0) || (item.Cards == 3))
                    continue;
                Goalkeepers.Add(new PlayerScores(item, weather, isCounter));
                Defenders.Add(new PlayerScores(item, weather, isCounter));
                Wingbacks.Add(new PlayerScores(item, weather, isCounter));
                Playmakers.Add(new PlayerScores(item, weather, isCounter));
                Wingers.Add(new PlayerScores(item, weather, isCounter));
                Forwards.Add(new PlayerScores(item, weather, isCounter));
            }

            Goalkeepers.Sort((p1, p2) => p2.GoalkeeperScore.CompareTo(p1.GoalkeeperScore));
            Defenders.Sort((p1, p2) => p2.DefenderScore.CompareTo(p1.DefenderScore));
            Wingbacks.Sort((p1, p2) => p2.WingbackScore.CompareTo(p1.WingbackScore));
            Playmakers.Sort((p1, p2) => p2.PlaymakerScore.CompareTo(p1.PlaymakerScore));
            Wingers.Sort((p1, p2) => p2.WingerScore.CompareTo(p1.WingerScore));
            Forwards.Sort((p1, p2) => p2.ForwardScore.CompareTo(p1.ForwardScore));
            #endregion
            List<PlayerScores> retVal = new List<PlayerScores>();
            if (Goalkeepers.Count == 0)
                return retVal; // No candidates left
            PlayerScores NAS = Goalkeepers[0];
            NAS.AssignedPosition = FieldPosition.Goalkeeper;
            addPlayerToLineup(retVal, NAS, Goalkeepers);

            addPlayersToLineup(retVal, howManyWingbacks, Wingbacks, FieldPosition.Wingback, Goalkeepers, Defenders, Wingbacks, Playmakers, Wingers, Forwards);
            addPlayersToLineup(retVal, howManyDefenders, Defenders, FieldPosition.Defender, Goalkeepers, Defenders, Wingbacks, Playmakers, Wingers, Forwards);
            addPlayersToLineup(retVal, howManyWingers, Wingers, FieldPosition.Winger, Goalkeepers, Defenders, Wingbacks, Playmakers, Wingers, Forwards);
            addPlayersToLineup(retVal, howManyPlaymakers, Playmakers, FieldPosition.Playmaker, Goalkeepers, Defenders, Wingbacks, Playmakers, Wingers, Forwards);
            addPlayersToLineup(retVal, howManyForwards, Forwards, FieldPosition.Forward, Goalkeepers, Defenders, Wingbacks, Playmakers, Wingers, Forwards);
            if (removeFromListAfterPickedLineup)
                removeUsedPlayers(players, retVal);
            return retVal;
        }

        private static void addPlayersToLineup(List<PlayerScores> lineup, int howManyPlayers, List<PlayerScores> listOfOptions,
            FieldPosition position, List<PlayerScores> Goalkeepers, List<PlayerScores> Defenders,
            List<PlayerScores> Wingbacks, List<PlayerScores> Playmakers, List<PlayerScores> Wingers, List<PlayerScores> Forwards)
        {
            PlayerScores NAS;
            for (int i = 0; i < howManyPlayers; i++)
            {
                if (listOfOptions.Count == 0)
                {
                    arrangeLineup(lineup, position, howManyPlayers - i);
                    return;
                }
                NAS = new PlayerScores(listOfOptions[0].TargetPlayer);
                NAS.AssignedPosition = position;
                listOfOptions.RemoveAt(0);
                if (isPlayerInPlayersList(lineup, NAS.TargetPlayer))
                {
                    if (listOfOptions.Count == 0) // no more compareable candidates
                    {
                        arrangeLineup(lineup, position, howManyPlayers - i);
                        return;
                    }
                    PlayerScores nextOption = getNextCandidate(lineup, listOfOptions);
                    if (nextOption == null)
                    {
                        arrangeLineup(lineup, position, howManyPlayers - i);
                        return;
                    }
                    nextOption.AssignedPosition = position;
                    PlayerScores nasFromLineup = getPlayerFromLineup(lineup, NAS.TargetPlayer);
                    switch (nasFromLineup.AssignedPosition)
                    {
                        case FieldPosition.Forward:
                            PlayerScores compareForward = getNextCandidate(lineup, Forwards);
                            if ((compareForward != null) && (compareForward.ForwardScore + NAS.AssignedPositionScore > NAS.ForwardScore + nextOption.AssignedPositionScore))
                                switchPlayerPositionAndAddReplacement(lineup, nasFromLineup, compareForward, position, Forwards, Goalkeepers, Defenders,
                                    Wingbacks, Playmakers, Wingers, Forwards);
                            else
                                i--;
                            break;
                        case FieldPosition.Winger:
                            PlayerScores compareWinger = getNextCandidate(lineup, Wingers);
                            if ((compareWinger != null) && (compareWinger.WingerScore + NAS.AssignedPositionScore > NAS.WingerScore + nextOption.AssignedPositionScore))
                                switchPlayerPositionAndAddReplacement(lineup, nasFromLineup, compareWinger, position, Wingers, Goalkeepers, Defenders,
                                    Wingbacks, Playmakers, Wingers, Forwards);
                            else
                                i--;
                            break;
                        case FieldPosition.Playmaker:
                            PlayerScores compareMidfield = getNextCandidate(lineup, Playmakers);
                            if ((compareMidfield != null) && (compareMidfield.PlaymakerScore + NAS.AssignedPositionScore > NAS.PlaymakerScore + nextOption.AssignedPositionScore))
                                switchPlayerPositionAndAddReplacement(lineup, nasFromLineup, compareMidfield, position, Playmakers, Goalkeepers, Defenders,
                                    Wingbacks, Playmakers, Wingers, Forwards);
                            else
                                i--;
                            break;
                        case FieldPosition.Wingback:
                            PlayerScores compareWingback = getNextCandidate(lineup, Wingbacks);
                            if ((compareWingback != null) && (compareWingback.WingbackScore + NAS.AssignedPositionScore > NAS.WingbackScore + nextOption.AssignedPositionScore))
                                switchPlayerPositionAndAddReplacement(lineup, nasFromLineup, compareWingback, position, Wingbacks, Goalkeepers, Defenders,
                                    Wingbacks, Playmakers, Wingers, Forwards);
                            else
                                i--;
                            break;
                        case FieldPosition.Defender:
                            PlayerScores compareDefender = getNextCandidate(lineup, Defenders);
                            if ((compareDefender != null) && (compareDefender.DefenderScore + NAS.AssignedPositionScore > NAS.DefenderScore + nextOption.AssignedPositionScore))
                                switchPlayerPositionAndAddReplacement(lineup, nasFromLineup, compareDefender, position, Defenders, Goalkeepers, Defenders,
                                    Wingbacks, Playmakers, Wingers, Forwards);
                            else
                                i--;
                            break;
                        case FieldPosition.Goalkeeper:
                            PlayerScores compareGoalkeeper = getNextCandidate(lineup, Goalkeepers);
                            if ((compareGoalkeeper != null) && (compareGoalkeeper.GoalkeeperScore + NAS.AssignedPositionScore > NAS.GoalkeeperScore + nextOption.AssignedPositionScore))
                                switchPlayerPositionAndAddReplacement(lineup, nasFromLineup, compareGoalkeeper, position, Goalkeepers, Goalkeepers, Defenders,
                                    Wingbacks, Playmakers, Wingers, Forwards);
                            else
                                i--;
                            break;
                    }
                }
                else
                {
                    addSelectedPlayerToLinup(lineup, NAS, position, Goalkeepers, Defenders, Wingbacks, Playmakers, Wingers, Forwards);
                }
            }
        }

        private static void addSelectedPlayerToLinup(List<PlayerScores> lineup,
                                                     PlayerScores NAS, 
                                                     FieldPosition position,
                                                     List<PlayerScores> Goalkeepers, 
                                                     List<PlayerScores> Defenders, 
                                                     List<PlayerScores> Wingbacks,
                                                     List<PlayerScores> Playmakers, 
                                                     List<PlayerScores> Wingers, 
                                                     List<PlayerScores> Forwards)
        {
            List<PlayerScores> candidates = new List<PlayerScores>();
            foreach (PlayerScores item in lineup)
            {
                if ((NAS.GetScoreByPosition(item.AssignedPosition) + item.GetScoreByPosition(position)) > (NAS.GetScoreByPosition(position) + item.AssignedPositionScore))
                    candidates.Add(item);
            }
            if (candidates.Count > 0)
            {
                candidates.Sort((p1, p2) => ((NAS.GetScoreByPosition(p2.AssignedPosition) + p2.GetScoreByPosition(position)) - (NAS.GetScoreByPosition(position) + p2.AssignedPositionScore)).CompareTo(
                    (NAS.GetScoreByPosition(p1.AssignedPosition) + p1.GetScoreByPosition(position)) - (NAS.GetScoreByPosition(position) + p1.AssignedPositionScore)));
                removePlayerFromLineup(lineup, candidates[0].TargetPlayer, Goalkeepers, Defenders, Wingbacks, Playmakers, Wingers, Forwards);
                NAS.AssignedPosition = candidates[0].AssignedPosition;
                candidates[0].AssignedPosition = position;
                switch (position)
                {
                    case FieldPosition.Forward:
                        addPlayerToLineup(lineup, candidates[0], Forwards);
                        break;
                    case FieldPosition.Winger:
                        addPlayerToLineup(lineup, candidates[0], Wingers);
                        break;
                    case FieldPosition.Playmaker:
                        addPlayerToLineup(lineup, candidates[0], Playmakers);
                        break;
                    case FieldPosition.Wingback:
                        addPlayerToLineup(lineup, candidates[0], Wingbacks);
                        break;
                    case FieldPosition.Defender:
                        addPlayerToLineup(lineup, candidates[0], Defenders);
                        break;
                    case FieldPosition.Goalkeeper:
                        addPlayerToLineup(lineup, candidates[0], Goalkeepers);
                        break;
                }
                AddPlayerToCandidates(NAS, Goalkeepers, Defenders, Wingbacks, Playmakers, Wingers, Forwards);
                switch (NAS.AssignedPosition)
                {
                    case FieldPosition.Forward:
                        addPlayersToLineup(lineup, 1, Forwards, NAS.AssignedPosition, Goalkeepers, Defenders, Wingbacks, Playmakers, Wingers, Forwards);
                        break;
                    case FieldPosition.Winger:
                        addPlayersToLineup(lineup, 1, Wingers, NAS.AssignedPosition, Goalkeepers, Defenders, Wingbacks, Playmakers, Wingers, Forwards);
                        break;
                    case FieldPosition.Playmaker:
                        addPlayersToLineup(lineup, 1, Playmakers, NAS.AssignedPosition, Goalkeepers, Defenders, Wingbacks, Playmakers, Wingers, Forwards);
                        break;
                    case FieldPosition.Wingback:
                        addPlayersToLineup(lineup, 1, Wingbacks, NAS.AssignedPosition, Goalkeepers, Defenders, Wingbacks, Playmakers, Wingers, Forwards);
                        break;
                    case FieldPosition.Defender:
                        addPlayersToLineup(lineup, 1, Defenders, NAS.AssignedPosition, Goalkeepers, Defenders, Wingbacks, Playmakers, Wingers, Forwards);
                        break;
                    case FieldPosition.Goalkeeper:
                        addPlayersToLineup(lineup, 1, Goalkeepers, NAS.AssignedPosition, Goalkeepers, Defenders, Wingbacks, Playmakers, Wingers, Forwards);
                        break;
                }
            }
            else
            {
                NAS.AssignedPosition = position;
                switch (position)
                {
                    case FieldPosition.Forward:
                        addPlayerToLineup(lineup, NAS, Forwards);
                        break;
                    case FieldPosition.Winger:
                        addPlayerToLineup(lineup, NAS, Wingers);
                        break;
                    case FieldPosition.Playmaker:
                        addPlayerToLineup(lineup, NAS, Playmakers);
                        break;
                    case FieldPosition.Wingback:
                        addPlayerToLineup(lineup, NAS, Wingbacks);
                        break;
                    case FieldPosition.Defender:
                        addPlayerToLineup(lineup, NAS, Defenders);
                        break;
                    case FieldPosition.Goalkeeper:
                        addPlayerToLineup(lineup, NAS, Goalkeepers);
                        break;
                }
            }
        }

        private static void addPlayerToLineup(List<PlayerScores> lineup, PlayerScores player, List<PlayerScores> candidates)
        {
            lineup.Add(player);
            RemovePlayerByName(player, candidates);
        }

        private static void removePlayerFromLineup(List<PlayerScores> lineup, Player player, List<PlayerScores> goalkeepers,
            List<PlayerScores> defenders, List<PlayerScores> wingbacks, List<PlayerScores> playmakers, List<PlayerScores> wingers,
            List<PlayerScores> forwards)
        {
            FieldPosition assignedPos = FieldPosition.Unknown;
            PlayerScores removedPlayer = null;
            for (int i = 0; i < lineup.Count; i++)
            {
                if (lineup[i].TargetPlayer == player)
                {
                    assignedPos = lineup[i].AssignedPosition;
                    removedPlayer = lineup[i];
                    removedPlayer.AssignedPosition = FieldPosition.Unknown;
                    lineup.RemoveAt(i);
                }
            }
            if (removedPlayer == null)
                return;
            switch (assignedPos)
            {
                case FieldPosition.Forward:
                    forwards.Add(removedPlayer);
                    forwards.Sort((p1, p2) => p2.ForwardScore.CompareTo(p1.ForwardScore));
                    break;
                case FieldPosition.Winger:
                    wingers.Add(removedPlayer);
                    wingers.Sort((p1, p2) => p2.WingerScore.CompareTo(p1.WingerScore));
                    break;
                case FieldPosition.Playmaker:
                    playmakers.Add(removedPlayer);
                    playmakers.Sort((p1, p2) => p2.PlaymakerScore.CompareTo(p1.PlaymakerScore));
                    break;
                case FieldPosition.Wingback:
                    wingbacks.Add(removedPlayer);
                    wingbacks.Sort((p1, p2) => p2.WingbackScore.CompareTo(p1.WingbackScore));
                    break;
                case FieldPosition.Defender:
                    defenders.Add(removedPlayer);
                    defenders.Sort((p1, p2) => p2.DefenderScore.CompareTo(p1.DefenderScore));
                    break;
                case FieldPosition.Goalkeeper:
                    goalkeepers.Add(removedPlayer);
                    goalkeepers.Sort((p1, p2) => p2.GoalkeeperScore.CompareTo(p1.GoalkeeperScore));
                    break;
                default:
                    break;
            }
            removedPlayer.AssignedPosition = assignedPos;
        }
        #region BLA
        public static List<PlayerScores> GetPenaltyShootoutOrder(List<PlayerScores> lineup)
        {
            List<PlayerScores> retVal = new List<PlayerScores>();
            for (int i = 0; i < lineup.Count; i++)
                retVal.Add(lineup[i]);

            retVal.Sort(delegate(PlayerScores p1, PlayerScores p2) { return p2.PenaltyShooterScore.CompareTo(p1.PenaltyShooterScore); });
            return retVal;
        }

        public static TeamPosition IsPlayerNeeded(List<Player> players, Player newPlayer, Weather weather, bool isCounter, int howManyWingbacks,
            int howManyDefenders, int howManyWingers, int howManyPlaymakers, int howManyForwards)
        {
            List<Player> workingCopyOfPlayers = new List<Player>(players);
            List<PlayerScores> testLineup = GetLineup(workingCopyOfPlayers, weather, isCounter, true, howManyWingbacks, howManyDefenders, howManyWingers, howManyPlaymakers, howManyForwards);
            if (isPlayerInPlayersList(testLineup, newPlayer))
                return TeamPosition.FirstTeam;
            testLineup = GetLineup(workingCopyOfPlayers, weather, isCounter, false, 0, 1, 1, 1, 1);
            if (isPlayerInPlayersList(testLineup, newPlayer))
                return TeamPosition.Substitute;
            return TeamPosition.Training;
        }

        private static double averagePositionScore(IEnumerable<PlayerScores> lineup, FieldPosition position)
        {
            var playersInPosition = (from player in lineup
                                     where player.AssignedPosition == position
                                     select player.AssignedPositionScore);
            return playersInPosition.Count() == 0 ? int.MaxValue : playersInPosition.Average();
        }

        // TODO: refactor later to use with lineupView to display average score per position for all positions
        public static FieldPosition GetWeakestPositionInLineup(List<PlayerScores> lineup)
        {
            var averageScorePerPosition = new[]
            {
                new { Position = FieldPosition.Forward,     AverageScore = averagePositionScore(lineup, FieldPosition.Forward) },
                new { Position = FieldPosition.Playmaker,    AverageScore = averagePositionScore(lineup, FieldPosition.Playmaker) },
                new { Position = FieldPosition.Winger,      AverageScore = averagePositionScore(lineup, FieldPosition.Winger)   },
                new { Position = FieldPosition.Wingback,    AverageScore = averagePositionScore(lineup, FieldPosition.Wingback) },
                new { Position = FieldPosition.Defender,    AverageScore = averagePositionScore(lineup, FieldPosition.Defender) },
                new { Position = FieldPosition.Goalkeeper,  AverageScore = averagePositionScore(lineup, FieldPosition.Goalkeeper) },
            };

            return (from positionAverage in averageScorePerPosition
                    orderby positionAverage.AverageScore
                    select positionAverage.Position).First();
        }

        public static Formation GetBestFormation(List<Player> players, Weather weather, bool isCounter)
        {
            Formation retVal = Formation.F_5_5_0;
            double maxScore = 0;
            List<PlayerScores> temp = new List<PlayerScores>();

            temp = GetLineup(players, weather, isCounter, false, 2, 3, 2, 3, 0);
            if (checkIfBetterFormation(players, temp, Formation.F_5_5_0, ref maxScore))
                retVal = Formation.F_5_5_0;

            temp = GetLineup(players, weather, isCounter, false, 2, 3, 1, 3, 1);
            if (checkIfBetterFormation(players, temp, Formation.F_5_4_1_1W, ref maxScore))
                retVal = Formation.F_5_4_1_1W;

            temp = GetLineup(players, weather, isCounter, false, 2, 3, 2, 2, 1);
            if (checkIfBetterFormation(players, temp, Formation.F_5_4_1_2W, ref maxScore))
                retVal = Formation.F_5_4_1_2W;

            temp = GetLineup(players, weather, isCounter, false, 2, 3, 0, 3, 2);
            if (checkIfBetterFormation(players, temp, Formation.F_5_3_2_0W, ref maxScore))
                retVal = Formation.F_5_3_2_0W;

            temp = GetLineup(players, weather, isCounter, false, 2, 3, 1, 2, 2);
            if (checkIfBetterFormation(players, temp, Formation.F_5_3_2_1W, ref maxScore))
                retVal = Formation.F_5_3_2_1W;

            temp = GetLineup(players, weather, isCounter, false, 2, 3, 2, 1, 2);
            if (checkIfBetterFormation(players, temp, Formation.F_5_3_2_2W, ref maxScore))
                retVal = Formation.F_5_3_2_2W;

            temp = GetLineup(players, weather, isCounter, false, 2, 3, 0, 2, 3);
            if (checkIfBetterFormation(players, temp, Formation.F_5_2_3_0W, ref maxScore))
                retVal = Formation.F_5_2_3_0W;

            temp = GetLineup(players, weather, isCounter, false, 2, 3, 1, 1, 3);
            if (checkIfBetterFormation(players, temp, Formation.F_5_2_3_1W, ref maxScore))
                retVal = Formation.F_5_2_3_1W;

            temp = GetLineup(players, weather, isCounter, false, 2, 3, 2, 0, 3);
            if (checkIfBetterFormation(players, temp, Formation.F_5_2_3_2W, ref maxScore))
                retVal = Formation.F_5_2_3_2W;

            temp = GetLineup(players, weather, isCounter, false, 1, 3, 2, 3, 1);
            if (checkIfBetterFormation(players, temp, Formation.F_4_5_1_1WB, ref maxScore))
                retVal = Formation.F_4_5_1_1WB;

            temp = GetLineup(players, weather, isCounter, false, 2, 2, 2, 3, 1);
            if (checkIfBetterFormation(players, temp, Formation.F_4_5_1_2WB, ref maxScore))
                retVal = Formation.F_4_5_1_2WB;

            temp = GetLineup(players, weather, isCounter, false, 1, 3, 1, 3, 2);
            if (checkIfBetterFormation(players, temp, Formation.F_4_4_2_1W_1WB, ref maxScore))
                retVal = Formation.F_4_4_2_1W_1WB;

            temp = GetLineup(players, weather, isCounter, false, 2, 2, 1, 3, 2);
            if (checkIfBetterFormation(players, temp, Formation.F_4_4_2_1W_2WB, ref maxScore))
                retVal = Formation.F_4_4_2_1W_2WB;

            temp = GetLineup(players, weather, isCounter, false, 1, 3, 2, 2, 2);
            if (checkIfBetterFormation(players, temp, Formation.F_4_4_2_2W_1WB, ref maxScore))
                retVal = Formation.F_4_4_2_2W_1WB;

            temp = GetLineup(players, weather, isCounter, false, 2, 2, 2, 2, 2);
            if (checkIfBetterFormation(players, temp, Formation.F_4_4_2_2W_2WB, ref maxScore))
                retVal = Formation.F_4_4_2_2W_2WB;

            temp = GetLineup(players, weather, isCounter, false, 1, 3, 0, 3, 3);
            if (checkIfBetterFormation(players, temp, Formation.F_4_3_3_0W_1WB, ref maxScore))
                retVal = Formation.F_4_3_3_0W_1WB;

            temp = GetLineup(players, weather, isCounter, false, 2, 2, 0, 3, 3);
            if (checkIfBetterFormation(players, temp, Formation.F_4_3_3_0W_2WB, ref maxScore))
                retVal = Formation.F_4_3_3_0W_2WB;

            temp = GetLineup(players, weather, isCounter, false, 1, 3, 1, 2, 3);
            if (checkIfBetterFormation(players, temp, Formation.F_4_3_3_1W_1WB, ref maxScore))
                retVal = Formation.F_4_3_3_1W_1WB;

            temp = GetLineup(players, weather, isCounter, false, 2, 2, 1, 2, 3);
            if (checkIfBetterFormation(players, temp, Formation.F_4_3_3_1W_2WB, ref maxScore))
                retVal = Formation.F_4_3_3_1W_2WB;

            temp = GetLineup(players, weather, isCounter, false, 1, 3, 2, 1, 3);
            if (checkIfBetterFormation(players, temp, Formation.F_4_3_3_2W_1WB, ref maxScore))
                retVal = Formation.F_4_3_3_2W_1WB;

            temp = GetLineup(players, weather, isCounter, false, 2, 2, 2, 1, 3);
            if (checkIfBetterFormation(players, temp, Formation.F_4_3_3_2W_2WB, ref maxScore))
                retVal = Formation.F_4_3_3_2W_2WB;

            temp = GetLineup(players, weather, isCounter, false, 0, 3, 2, 3, 2);
            if (checkIfBetterFormation(players, temp, Formation.F_3_5_2_0WB, ref maxScore))
                retVal = Formation.F_3_5_2_0WB;

            temp = GetLineup(players, weather, isCounter, false, 1, 2, 2, 3, 2);
            if (checkIfBetterFormation(players, temp, Formation.F_3_5_2_1WB, ref maxScore))
                retVal = Formation.F_3_5_2_1WB;

            temp = GetLineup(players, weather, isCounter, false, 2, 1, 2, 3, 2);
            if (checkIfBetterFormation(players, temp, Formation.F_3_5_2_2WB, ref maxScore))
                retVal = Formation.F_3_5_2_2WB;

            temp = GetLineup(players, weather, isCounter, false, 0, 3, 1, 3, 3);
            if (checkIfBetterFormation(players, temp, Formation.F_3_4_3_1W_0WB, ref maxScore))
                retVal = Formation.F_3_4_3_1W_0WB;

            temp = GetLineup(players, weather, isCounter, false, 1, 2, 1, 3, 3);
            if (checkIfBetterFormation(players, temp, Formation.F_3_4_3_1W_1WB, ref maxScore))
                retVal = Formation.F_3_4_3_1W_1WB;

            temp = GetLineup(players, weather, isCounter, false, 2, 1, 1, 3, 3);
            if (checkIfBetterFormation(players, temp, Formation.F_3_4_3_1W_2WB, ref maxScore))
                retVal = Formation.F_3_4_3_1W_2WB;

            temp = GetLineup(players, weather, isCounter, false, 0, 3, 2, 2, 3);
            if (checkIfBetterFormation(players, temp, Formation.F_3_4_3_2W_0WB, ref maxScore))
                retVal = Formation.F_3_4_3_2W_0WB;

            temp = GetLineup(players, weather, isCounter, false, 1, 2, 2, 2, 3);
            if (checkIfBetterFormation(players, temp, Formation.F_3_4_3_2W_1WB, ref maxScore))
                retVal = Formation.F_3_4_3_2W_1WB;

            temp = GetLineup(players, weather, isCounter, false, 2, 1, 2, 2, 3);
            if (checkIfBetterFormation(players, temp, Formation.F_3_4_3_2W_2WB, ref maxScore))
                retVal = Formation.F_3_4_3_2W_2WB;

            temp = GetLineup(players, weather, isCounter, false, 0, 2, 2, 3, 3);
            if (checkIfBetterFormation(players, temp, Formation.F_2_5_3_0WB, ref maxScore))
                retVal = Formation.F_2_5_3_0WB;

            temp = GetLineup(players, weather, isCounter, false, 1, 1, 2, 3, 3);
            if (checkIfBetterFormation(players, temp, Formation.F_2_5_3_1WB, ref maxScore))
                retVal = Formation.F_2_5_3_1WB;

            temp = GetLineup(players, weather, isCounter, false, 2, 0, 2, 3, 3);
            if (checkIfBetterFormation(players, temp, Formation.F_2_5_3_2WB, ref maxScore))
                retVal = Formation.F_2_5_3_2WB;

            return retVal;
        }

        private static bool checkIfBetterFormation(List<Player> players, List<PlayerScores> lineup, Formation formation, ref double maxScore)
        {
            double nextFormationScore = GetFormationScore(lineup);
            bool retVal = false;
            if (nextFormationScore > maxScore)
            {
                maxScore = nextFormationScore;
                retVal = true;
            }
            return retVal;
        }

        private static double GetFormationScore(List<PlayerScores> lineup)
        {
            double retVal = 0;
            foreach (PlayerScores item in lineup)
            {
                retVal += item.AssignedPositionScore;
            }
            return retVal;
        }

        public static void GetPlayerCountPerPosition(Formation formation, out int wingbacks, out int defenders, out int wingers, out int playmakers, out int forwards)
        {
            switch (formation)
            {
                case Formation.F_5_5_0:
                    wingbacks = 2;
                    defenders = 3;
                    wingers = 2;
                    playmakers = 3;
                    forwards = 0;
                    break;
                case Formation.F_5_4_1_1W:
                    wingbacks = 2;
                    defenders = 3;
                    wingers = 1;
                    playmakers = 3;
                    forwards = 1;
                    break;
                case Formation.F_5_4_1_2W:
                    wingbacks = 2;
                    defenders = 3;
                    wingers = 2;
                    playmakers = 2;
                    forwards = 1;
                    break;
                case Formation.F_5_3_2_0W:
                    wingbacks = 2;
                    defenders = 3;
                    wingers = 0;
                    playmakers = 3;
                    forwards = 2;
                    break;
                case Formation.F_5_3_2_1W:
                    wingbacks = 2;
                    defenders = 3;
                    wingers = 1;
                    playmakers = 2;
                    forwards = 2;
                    break;
                case Formation.F_5_3_2_2W:
                    wingbacks = 2;
                    defenders = 3;
                    wingers = 2;
                    playmakers = 1;
                    forwards = 2;
                    break;
                case Formation.F_5_2_3_0W:
                    wingbacks = 2;
                    defenders = 3;
                    wingers = 0;
                    playmakers = 2;
                    forwards = 3;
                    break;
                case Formation.F_5_2_3_1W:
                    wingbacks = 2;
                    defenders = 3;
                    wingers = 1;
                    playmakers = 1;
                    forwards = 3;
                    break;
                case Formation.F_5_2_3_2W:
                    wingbacks = 2;
                    defenders = 3;
                    wingers = 2;
                    playmakers = 0;
                    forwards = 3;
                    break;
                case Formation.F_4_5_1_1WB:
                    wingbacks = 1;
                    defenders = 3;
                    wingers = 2;
                    playmakers = 3;
                    forwards = 1;
                    break;
                case Formation.F_4_5_1_2WB:
                    wingbacks = 2;
                    defenders = 2;
                    wingers = 2;
                    playmakers = 3;
                    forwards = 1;
                    break;
                case Formation.F_4_4_2_1W_1WB:
                    wingbacks = 1;
                    defenders = 3;
                    wingers = 1;
                    playmakers = 3;
                    forwards = 2;
                    break;
                case Formation.F_4_4_2_1W_2WB:
                    wingbacks = 2;
                    defenders = 2;
                    wingers = 1;
                    playmakers = 3;
                    forwards = 2;
                    break;
                case Formation.F_4_4_2_2W_1WB:
                    wingbacks = 1;
                    defenders = 3;
                    wingers = 2;
                    playmakers = 2;
                    forwards = 2;
                    break;
                case Formation.F_4_4_2_2W_2WB:
                    wingbacks = 2;
                    defenders = 2;
                    wingers = 2;
                    playmakers = 2;
                    forwards = 2;
                    break;
                case Formation.F_4_3_3_0W_1WB:
                    wingbacks = 1;
                    defenders = 3;
                    wingers = 0;
                    playmakers = 3;
                    forwards = 3;
                    break;
                case Formation.F_4_3_3_0W_2WB:
                    wingbacks = 2;
                    defenders = 2;
                    wingers = 0;
                    playmakers = 3;
                    forwards = 3;
                    break;
                case Formation.F_4_3_3_1W_1WB:
                    wingbacks = 1;
                    defenders = 3;
                    wingers = 1;
                    playmakers = 2;
                    forwards = 3;
                    break;
                case Formation.F_4_3_3_1W_2WB:
                    wingbacks = 2;
                    defenders = 2;
                    wingers = 1;
                    playmakers = 2;
                    forwards = 3;
                    break;
                case Formation.F_4_3_3_2W_1WB:
                    wingbacks = 1;
                    defenders = 3;
                    wingers = 2;
                    playmakers = 1;
                    forwards = 3;
                    break;
                case Formation.F_4_3_3_2W_2WB:
                    wingbacks = 2;
                    defenders = 2;
                    wingers = 2;
                    playmakers = 1;
                    forwards = 3;
                    break;
                case Formation.F_3_5_2_0WB:
                    wingbacks = 0;
                    defenders = 3;
                    wingers = 2;
                    playmakers = 3;
                    forwards = 2;
                    break;
                case Formation.F_3_5_2_1WB:
                    wingbacks = 1;
                    defenders = 2;
                    wingers = 2;
                    playmakers = 3;
                    forwards = 2;
                    break;
                case Formation.F_3_5_2_2WB:
                    wingbacks = 2;
                    defenders = 1;
                    wingers = 2;
                    playmakers = 3;
                    forwards = 2;
                    break;
                case Formation.F_3_4_3_1W_0WB:
                    wingbacks = 0;
                    defenders = 3;
                    wingers = 1;
                    playmakers = 3;
                    forwards = 3;
                    break;
                case Formation.F_3_4_3_1W_1WB:
                    wingbacks = 1;
                    defenders = 2;
                    wingers = 1;
                    playmakers = 3;
                    forwards = 3;
                    break;
                case Formation.F_3_4_3_1W_2WB:
                    wingbacks = 2;
                    defenders = 1;
                    wingers = 1;
                    playmakers = 3;
                    forwards = 3;
                    break;
                case Formation.F_3_4_3_2W_0WB:
                    wingbacks = 0;
                    defenders = 3;
                    wingers = 2;
                    playmakers = 2;
                    forwards = 3;
                    break;
                case Formation.F_3_4_3_2W_1WB:
                    wingbacks = 1;
                    defenders = 2;
                    wingers = 2;
                    playmakers = 2;
                    forwards = 3;
                    break;
                case Formation.F_3_4_3_2W_2WB:
                    wingbacks = 2;
                    defenders = 1;
                    wingers = 2;
                    playmakers = 2;
                    forwards = 3;
                    break;
                case Formation.F_2_5_3_0WB:
                    wingbacks = 0;
                    defenders = 2;
                    wingers = 2;
                    playmakers = 3;
                    forwards = 3;
                    break;
                case Formation.F_2_5_3_1WB:
                    wingbacks = 1;
                    defenders = 1;
                    wingers = 2;
                    playmakers = 3;
                    forwards = 3;
                    break;
                case Formation.F_2_5_3_2WB:
                    wingbacks = 2;
                    defenders = 0;
                    wingers = 2;
                    playmakers = 3;
                    forwards = 3;
                    break;
                default:
                    throw new ArgumentException("invalid formation", "formation");
            }
        }
        private static void arrangeLineup(List<PlayerScores> lineup, FieldPosition position, int numOfMissingPlayers)
        {
            List<PlayerScores> newCandidates = new List<PlayerScores>();
            foreach (PlayerScores item in lineup)
            {
                if (item.GetScoreByPosition(position) > item.AssignedPositionScore)
                    newCandidates.Add(item);
            }
            if (newCandidates.Count == 0)
                return;
            newCandidates.Sort((p1, p2) => (p2.GetScoreByPosition(position) - p2.AssignedPositionScore).CompareTo(p1.GetScoreByPosition(position) - p1.AssignedPositionScore));
            for (int i = 0; i < Math.Min(numOfMissingPlayers, newCandidates.Count); i++)
            {
                ChangePlayerAssignment(lineup, newCandidates[i], position);
            }
        }

        private static void ChangePlayerAssignment(List<PlayerScores> lineup, PlayerScores playerInLineup, FieldPosition position)
        {
            int i = 0;
            while ((i < lineup.Count) && (lineup[i] != playerInLineup))
                i++;
            if (i < lineup.Count)
                lineup[i].AssignedPosition = position;
        }

        private static void removeUsedPlayers(List<Player> players, ICollection<PlayerScores> lineup)
        {
            foreach (PlayerScores item in lineup)
                players.Remove(item.TargetPlayer);
        }

        public static PlayerScores GetCaptain(List<PlayerScores> lineup)
        {
            if (lineup.Count == 0)
                throw new Exception("No players to choose from");

            PlayerScores retVal = lineup[0];
            double value = lineup[0].CaptainScore;

            for (int i = 1; i < lineup.Count; i++)
            {
                if (lineup[i].CaptainScore > value)
                {
                    retVal = lineup[i];
                    value = lineup[i].CaptainScore;
                }
            }
            return retVal;
        }

        public static PlayerScores GetSetpiecesTaker(List<PlayerScores> lineup)
        {
            List<PlayerScores> fieldPlayers = new List<PlayerScores>();
            foreach (PlayerScores item in lineup)
            {
                if ((item.AssignedPosition != FieldPosition.Unknown) && (item.AssignedPosition != FieldPosition.Goalkeeper))
                    fieldPlayers.Add(item);
            }
            if (fieldPlayers.Count == 0)
                throw new Exception("No Set Pieces Candidates Left");
            PlayerScores retVal = fieldPlayers[0];
            double value = fieldPlayers[0].SetPiecesScore;

            for (int i = 1; i < fieldPlayers.Count; i++)
            {
                if (fieldPlayers[i].SetPiecesScore > value)
                {
                    retVal = fieldPlayers[i];
                    value = fieldPlayers[i].SetPiecesScore;
                }
            }
            return retVal;
        }
        #endregion
    }
}
